<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>编写服务器</title>
  </head>

  <body>
    <h1>编写服务器</h1>

    <h2>概述</h2>

    <p>Twisted是一个非常灵活的框架，能让你编写出强大的服务器。而灵活的代价就是编写服务器时，需要分好几层。</p>

    <p>这篇文档描述了协议（<code class="API" base="twisted.internet.protocol">Protocol</code>）层，我们的程序要在这里依照既定协议对数据进行分析和处理。如果你在写一个应用程序，那么在读这篇文档之前，你应该先在<a href="plugin.xhtml">《编写Twisted插件》</a>里，读一下关于如何开始编写Twisted应用程序的总体概述。这篇文档只讨论TCP、SSL与Unix套接字服务器，有<a href="udp.xhtml">另外一篇文档</a>讨论UDP。</p>

    <p>你的协议处理类通常是<code class="API">twisted.internet.protocol.Protocol</code>的子类。大多数协议处理程序要么继承自这个类，要么继承自它的某个更便捷的子类。系统会根据需要，为每个连接创建一个协议类的实例，连接断开后就不用了。这就意味着<code>Protocol</code>里没存持久配置（比如服务器端口等——译者注）。</p>

    <p>持久配置由一个工厂类来保管，这个工厂类通常继承自<code class="API">twisted.internet.protocol.Factory</code>。默认的工厂类仅仅把<code>Protocol</code>实例化，并且给每个实例设置一个指向工厂自己的属性<code>factory</code>。这样每个<code>Protocol</code>都可以访问持久配置，并且可以修改他们。</p>

    <p>在多个端口或是不同网络地址上提供一样的服务通常是很有用处的。这就是为什么<code>Factory</code>不监听连接，并且实际上对网络一无所知。更多信息见<code class="API">twisted.internet.interfaces.IReactorTCP.listenTCP</code>，以及其他的<code>IReactor*.listen*</code>API。</p>

    <p>这篇文档会解释上面的每一步。</p>

    <h2>协议</h2>

    <p>正如之前提到的，大部分的代码都在协议与一些辅助的类和函数中。Twisted的协议程序以异步的方式来处理数据，这就是说，协议程序从来不会去等待一个事件，而是当网络上的事件到达时再去响应。</p>

    <p>这里有一个简单的例子：</p>
<pre class="python">
from twisted.internet.protocol import Protocol

class Echo(Protocol):

    def dataReceived(self, data):
        self.transport.write(data)
</pre>

    <p>这是一个最简单的协议，它只是把收到的内容写回去，并没有响应所有的事件。下面这个例子中响应了另一个事件：</p>
<pre class="python">
from twisted.internet.protocol import Protocol

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write("一天一苹果，医生不缠我\r\n") 
        self.transport.loseConnection()
</pre>

    <p>这个协议在建立连接之后，发送一条谚语（原文是“An apple a day keeps the doctor away”——译者注），然后断开连接。</p>

	<p><code>connectionMade</code>事件通常发生在连接对象构建的时候，以及任何初始化握手时（像上面的QOTD协议，其实它是根据RFC 865写的（RFC 865就是Quote of the Day Protocol——译者注））。<code>connectionLost</code>事件发生在连接特定的各种对象销毁时。这里有个例子：</p>
<pre class="python">
from twisted.internet.protocol import Protocol

class Echo(Protocol):

    def connectionMade(self):
        self.factory.numProtocols = self.factory.numProtocols+1 
        if self.factory.numProtocols &gt; 100:
            self.transport.write("哦唷，连接太多啦！过会儿再试试吧。") 
            self.transport.loseConnection()

    def connectionLost(self, reason):
        self.factory.numProtocols = self.factory.numProtocols-1

    def dataReceived(self, data):
        self.transport.write(data)
</pre>

    <p>例子中，<code>connectionMade</code>和<code>connectionLost</code>一起维护一个计数器，跟踪工厂中活动协议的数量。一旦计数器显示超过警戒值（例子中的“100”），任何新的连接都会被<code>connectionMade</code>当场关闭。</p>

    <h3>使用协议</h3>

    <p>在这一节中，我会说明如何简单地试验你的协议。（然而，要写出产品级的Twisted服务器，你应该也读一下<a href="plugin.xhtml" >编写Twisted插件</a>的指南）。</p>

    <p>下面的代码会运行前面讨论过的QOTD服务器：</p>
<pre class="python">
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write("An apple a day keeps the doctor away\r\n") 
        self.transport.loseConnection()

# 魔术发生在下面一行：
factory = Factory()
factory.protocol = QOTD

# 8007是想要跑服务器的端口。随便选个&gt;1024的都行
reactor.listenTCP(8007, factory)
reactor.run()
</pre>

    <p>不必担心最后6行魔法般的代码——读到后面你就知道他们是干嘛的了。</p>

    <h3>助手协议</h3>

    <p>许多协议的底层抽象都是类似的，网上最流行的就数按行分的了——通常每一行都结束于一个回车－换行的组合。</p>

    <p>然而，也有很小一部分协议是混合的——他们包含分行的部分，也有原始数据的部分。这样的例子包括HTTP/1.1和Freenet协议。</p>

    <p><code>LineReceiver</code>协议适用于这些情况，它可以把事件分发给两个不同的处理器——<code>lineReceived</code>和<code>rawDataReceived</code>。默认地，协议只会使用<code>lineReceived</code>（一行数据调用一次），但是你可以用<code>setRawMode</code>和<code>setLineMode</code>来调整协议的处理器。</p>

    <p>这里有个按行接收的简单示例：</p>
<pre class="python">
from twisted.protocols.basic import LineReceiver

class Answer(LineReceiver):

    answers = {'你好吗？': '很好', None : "不懂你说的啥"}

    def lineReceived(self, line):
        if self.answers.has_key(line):
            self.sendLine(self.answers[line])
        else:
            self.sendLine(self.answers[None])
</pre>

    <p>注意，行分割符（回车－换行）并不在line中。</p>

    <p>另外还有一些其他不是特别常用的助手协议，比如说基于netstring的协议，还有定长协议。</p>

    <h3>状态机</h3>

    <p>许多Twisted协议处理器需要实现状态机来记录它目前的状态，这里有一些关于实现状态机的建议：</p>

    <ul>
      <li>别写巨型状态机，写一次只处理一层抽象的最好了。</li>

      <li>使用Python的动态特性来创建open ended（什么意思？）的状态机。可以参考——比如说——SMTP客户端的代码。</li>

      <li>不要把应用逻辑与协议处理代码混在一起。如果协议处理器必须要调用应用逻辑的话，那就尽量用方法调用。</li>
    </ul>

    <h2>工厂</h2>

    <p>正如前面提到的，通常情况下只用<code class="API">twisted.internet.protocol.Factory</code>类就行了，不必派生子类。但有些时候，协议可能会有工厂相关配置，或是其他的考虑，这时候就有必要派生<code>Factory</code>的子类了。</p>

    <p>对于一个只是“生产”协议的工厂来说，只需要实例化一个<code>Factory</code>，并且设置它的<code>protocol</code>属性：</p>
<pre class="python">
from twisted.internet.protocol import Factory
from twisted.protocols.wire import Echo

myFactory = Factory()
myFactory.protocol = Echo
</pre>

    <p>如果需要方便地为特殊配置构造工厂，那么一个工厂函数总是很有用的：</p>
<pre class="python">
from twisted.internet.protocol import Factory, Protocol

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write(self.factory.quote+'\r\n')
        self.transport.loseConnection()


def makeQOTDFactory(quote=None):
    factory = Factory()
    factory.protocol = QOTD
    factory.quote = quote or '一天一苹果，医生不缠我'
    return factory
</pre>

    <p>在工厂的<code>__init__</code>和<code>__del__</code>里写一些
    A Factory has two methods to perform application-specific
    building up and tearing down (since a Factory is frequently
    persisted, it is often not appropriate to do them in <code>__init__</code>
    or <code>__del__</code>, and would frequently be too early or too late).</p>

    <p>Here is an example of a factory which allows its Protocols
    to write to a special log-file:</p>
<pre class="python">
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver


class LoggingProtocol(LineReceiver):

    def lineReceived(self, line):
        self.factory.fp.write(line+'\n')


class LogfileFactory(Factory):

    protocol = LoggingProtocol

    def __init__(self, fileName):
        self.file = fileName

    def startFactory(self):
        self.fp = open(self.file, 'a')

    def stopFactory(self):
        self.fp.close()
</pre>

    <h3>Putting it All Together</h3>

    <p>So, you know what factories are, and want to run the QOTD
    with configurable quote server, do you? No problems, here is an
    example.</p>

<pre class="python">
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor

class QOTD(Protocol):

    def connectionMade(self):
        self.transport.write(self.factory.quote+'\r\n')
        self.transport.loseConnection()


class QOTDFactory(Factory):

    protocol = QOTD

    def __init__(self, quote=None):
        self.quote = quote or 'An apple a day keeps the doctor away'

reactor.listenTCP(8007, QOTDFactory("configurable quote"))
reactor.run()
</pre>

    <p>The only lines you might not understand are the last two.</p>

<p><code class="API"
base="twisted.internet.interfaces.IReactorTCP">listenTCP</code> is
the method which connects a <code>Factory</code> to the network.
It uses the reactor interface, which lets many different loops handle
the networking code, without modifying end-user code, like this.
As mentioned above, if you want to write your code to be a production-grade
Twisted server, and not a mere 20-line hack, you will want to
use <a href="application.xhtml">the Application object</a>.</p>

  </body>
</html>
